/*
 * @Description: 场馆地图绘制
 * @Author: zy
 * @Date: 2021-03-29 15:32:54
 * @LastEditors: zy
 * @LastEditTime: 2021-04-14 17:02:10
 */
import * as d3 from 'd3'

export class VenueCanvas {
  chart = {}

  saveChart = {
    width: 0,
    height: 0,
    areas: []
  }

  constructor (divId) {
    this.divId = divId
  }

  initChart () {
    this.chart.padding = {
      left: 0,
      right: 0,
      top: 0,
      bottom: 0
    }
    this.chart.divHeight = document.getElementById(this.divId).offsetHeight -
      this.chart.padding.top -
      this.chart.padding.bottom
    this.chart.divWidth = document.getElementById(this.divId).offsetWidth -
      this.chart.padding.left -
      this.chart.padding.right

    this.chart.svg = d3
      .select('#' + this.divId)
      .append('svg')
      .attr('width', this.chart.divWidth +
        this.chart.padding.left +
        this.chart.padding.right
      )
      .attr('height', this.chart.divHeight +
        this.chart.padding.top +
        this.chart.padding.bottom
      )
      .attr('class', 'svg_chart')
  }

  renderChart (width, height, sensors) {
    this.initAxis(width, height)
    this.renderGrid(width, height)
    this.renderSensor(sensors)
  }
  /**
   * 初始化坐标轴
   * @param {int} width 场地的长度
   * @param {int} height 长度的宽度
   */
  initAxis (width, height) {
    this.saveChart.width = width
    this.saveChart.height = height

    this.chart.xScale = d3.scaleLinear()
      .domain([0, width])
      .range([0, this.chart.divWidth])

    this.chart.yScale = d3.scaleLinear()
      .domain([0, height])
      .range([0, this.chart.divHeight])

    this.chart.xAxis = d3.axisTop()
      .scale(this.chart.xScale)
      .ticks(width * 2)

    this.chart.yAxis = d3.axisLeft()
      .scale(this.chart.yScale)
      .ticks(height * 2)

    if (!this.chart.svg.select('.xAxis').empty()) {
      this.chart.svg.select('.xAxis').remove()
    }
    if (!this.chart.svg.select('.yAxis').empty()) {
      this.chart.svg.select('.yAxis').remove()
    }
    this.chart.svg.append('g')
      .attr('class', 'xAxis')
      .call(this.chart.xAxis)
    this.chart.svg.append('g')
      .attr('class', 'yAxis')
      .call(this.chart.yAxis)
  }
  /**
   * 渲染网格
   * @param {int} width 场地的长度
   * @param {int} height 长度的宽度
   */
  renderGrid (width, height) {
    this.chart.svg.selectAll('.row').remove()
    let grids = []
    for (let i = 0; i < height; ++i) {
      grids.push(d3.range(0, width, 1))
    }
    this.chart.rectWidth = this.chart.xScale(grids[0][1]) - this.chart.xScale(grids[0][0])
    this.chart.rectHeight = this.chart.yScale(grids[0][1]) - this.chart.yScale(grids[0][0])

    this.chart.gridArea = this.chart.svg.append('g')
      .attr('class', 'grids')
    this.chart.rects = this.chart.gridArea.selectAll('.row')
      .data(grids)
      .enter()
      .append('g')
      .attr('class', 'row')
      .selectAll('rect')
      .data(function (d, i) {
        return d.map(function (_, j) {
          return {'x': j, 'y': i, 'name': ''}
        })
      })
      .enter()
      .append('rect')
      .attr('class', d => d.x + '-' + d.y)
      .attr('x', d => this.chart.xScale(d.x))
      .attr('y', d => this.chart.yScale(d.y))
      .attr('width', this.chart.rectWidth)
      .attr('height', this.chart.rectHeight)
      .style('stroke', 'black')
      .style('stroke-width', 0.5)
      .style('fill', 'gray')
      .style('opacity', 0.7)
  }
  /**
   * 绘制传感器的位置
   * @param {[]} sensors 传感器列表
   */
  renderSensor (sensors) {
    let gSensor = this.chart.gridArea
      .append('g')
      .attr('class', 'sensors')
      .selectAll('g')
      .data(sensors)
      .enter()
      .append('g')
      .attr('class', function (d) {
        d.peopleNum = 0
        return d.sid
      })

    gSensor.append('rect')
      .attr('x', d => this.chart.xScale(d.y))
      .attr('y', d => this.chart.yScale(d.x))
      .attr('width', this.chart.rectWidth)
      .attr('height', this.chart.rectHeight)
      .style('stroke', 'black')
      .style('stroke-width', 0.5)
      .style('fill', 'white')
      .style('opacity', 1)

    gSensor.append('text')
      .attr('x', d => this.chart.xScale(d.y) + this.chart.rectWidth * 0.4)
      .attr('y', d => this.chart.yScale(d.x) + this.chart.rectHeight * 0.6)
  }
  /**
   * 设置框选刷
   * @param {*} content panel中显示的起点和终点及场地名称信息
   */
  brush (content) {
    let brush = d3.brush()
      .extent([[this.chart.padding.left, this.chart.padding.top], [this.chart.divWidth, this.chart.divHeight]])
      .on('brush', durationBrush)
      .on('end', brushEnd)

    this.chart.svg.append('g')
      .attr('class', 'brush')
      .call(brush)

    let chart = this.chart
    let that = this

    let width = d3.max(chart.xScale.domain())
    let height = d3.max(chart.yScale.domain())

    function durationBrush (event) {
      let selection = event.selection
      if (!event.sourceEvent || !selection) return
      let range = that.getCoverArea(selection)
      let xExtents = range.x
      let yExtents = range.y

      that.setSelection(xExtents, yExtents)

      content.start = xExtents[0] + ',' + (yExtents[0])
      content.end = (xExtents[1] === width - 1 ? xExtents[1] : xExtents[1] - 1) + ',' +
       (yExtents[1] === height - 1 ? yExtents[1] : yExtents[1] - 1)
    }

    function brushEnd (event) {
      let selection = event.selection
      if (!event.sourceEvent || !selection) return
      let range = that.getCoverArea(selection)
      let xExtents = range.x
      let yExtents = range.y

      let coverRange = [[chart.xScale(xExtents[0]), chart.yScale(yExtents[0])],
        [chart.xScale((xExtents[1] === width - 1 ? xExtents[1] + 1 : xExtents[1])),
          chart.yScale(yExtents[1] === height - 1 ? yExtents[1] + 1 : yExtents[1])]]

      d3.select(this)
        .call(brush.move, coverRange)

      that.setSelection(xExtents, yExtents)
    }
  }
  /**
   * 设置并显示场地名称
   * @param {string} name 场地名称
   * @param {[[]]} selection 场地区域
   */
  setAreaName (name, id, selection) {
    let area = {
      'startPoint': selection[0],
      'endPoint': selection[1],
      'name': name
    }
    this.saveChart.areas.push(area)
    let width = selection[1][0] - selection[0][0]
    let height = selection[1][1] - selection[0][1]
    let fontSize = 16

    let areaChart = this.chart.gridArea
      .append('g')
      .datum({'id': id, 'name': name})
      .attr('id', 'area-' + id)
      .attr('class', 'area')

    areaChart.append('rect')
      .attr('x', selection[0][0])
      .attr('y', selection[0][1])
      .attr('width', width)
      .attr('height', height)
      .style('stroke', 'black')
      .style('stroke-width', 3)
      .style('fill', 'white')
      .style('opacity', 0.5)

    areaChart.append('text')
      .attr('x', selection[0][0] + width / 2 - name.length / 2 * fontSize)
      .attr('y', selection[0][1] + height / 2)
      .text(d => d.name)
      .attr('font-size', fontSize)

    this.chart.svg.selectAll('.brush').remove()
  }
  /**
   * 获取框选刷取覆盖的矩形的区域
   * @param {[[]]} selection 框选区域
   * @returns 覆盖的矩形区域
   */
  getCoverArea (selection) {
    let xp = []
    let yp = []
    let that = this
    this.chart.rects.each(function (d) {
      if (that.contains(selection, d)) {
        xp.push(parseFloat(d.x))
        yp.push(parseFloat(d.y))
      }
    })
    let xExtents = d3.extent(xp)
    let yExtents = d3.extent(yp)
    return {'x': xExtents, 'y': yExtents}
  }
  /**
   * 判断该矩形是否杯覆盖
   * @param {[[]]} range 框选区域
   * @param {*} d 矩形坐标
   * @returns true or flase
   */
  contains (range, d) {
    let chart = this.chart
    return range[0][0] <= chart.xScale(d.x) && chart.xScale(d.x) <= range[1][0] &&
       range[0][1] <= chart.yScale(d.y) && chart.yScale(d.y) <= range[1][1]
  }
  /**
   * 设置选中区域颜色
   * @param {[]} xExtents 选中区域的x坐标
   * @param {[]} yExtents 选中区域的y坐标
   */
  setSelection (xExtents, yExtents) {
    let chart = this.chart
    let width = d3.max(chart.xScale.domain())
    let height = d3.max(chart.yScale.domain())

    let rectRange = [[chart.xScale(xExtents[0]), chart.yScale(yExtents[0])],
      [chart.xScale((xExtents[1] === width - 1 ? xExtents[1] : xExtents[1] - 1)),
        chart.yScale(yExtents[1] === height - 1 ? yExtents[1] : yExtents[1] - 1)]]

    let rects = chart.rects.filter(d => d.name === '')
    rects.style('fill', rectRange && (d => (this.contains(rectRange, d) ? 'red' : null)))
  }
  /**
   * 绘制所有的场所
   * @param {[]} areas 场所列表
   */
  setAllArea (areas) {
    for (let i = 0; i < areas.length; ++i) {
      this.setArea(areas[i])
    }
  }
  /**
   * 绘制单个场所
   * @param {} area 场地对象
   */
  setArea (area) {
    let selection = [[this.chart.xScale(area.startX), this.chart.yScale(area.startY)],
      [this.chart.xScale(area.endX), this.chart.yScale(area.endY)]]
    this.setAreaName(area.areaName, area.id, selection)
  }
}
